/*
 * Copyright (C) 2008+ Saifeng Zeng
 *		 2008+ Spark Zheng
 *				 
 * @file	test/test_mempool.c
 * @brief
 *	Usage: test_mempool [s(sequence)|r(random)|f(fixed-size)|
 *			     S(mutli-thread sequence)|
 *			     R(mutli-thread random)|
 *			     F(mutli-thread fixed-size)|v(validation)]
 *	marena test: check test_htable.c and utils/htable.c
 *
 * @history:
 * 	version 0.1.0	Saifeng add initial version for sequence/validation test
 *	version 0.2.0	Spark add random/fixed-size/S/R/F test
 *	version 0.2.2	Spark add rbtree mempool test and factor mempool test
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mempool.h"
#include "timer.h"
#include "random.h"
#include "threadpool.h"

#define ARRAY_LEN		100 * 1000
#define LOOP_NUM		300
#define TP_ARRAY_LEN		10 * 100
#define TP_LOOP_NUM		300

dlib_mpool_t *mp;

static void *_tp_mp_malloc_worker(void *targ, void *arg);
static void *_tp_mp_free_worker(void *targ, void *arg);
static void *_tp_glibc_malloc_worker(void *targ, void *arg);
static void *_tp_glibc_free_worker(void *targ, void *arg);

int main(int argc, char *argv[])
{
	struct timeval start, end, result;

	uint32_t min = 16;
	uint32_t max = 4096;

	uint32_t max_chunk_size = max + 16;
	
#ifdef _DLIB_MPOOL_USE_FACTOR
	float factor = 2;
#else
	uint32_t factor = 16;
#endif

	int i, j;
	char *array[ARRAY_LEN];

	if (argc < 2) {
		fprintf(stderr,
		"Usage: test_mempool [s(sequence)|r(random)|f(fixed-size)|\n"
		"		     S(mutli-thread sequence)|\n"
		"		     R(mutli-thread random)|\n"
		"		     F(mutli-thread fixed-size)|v(validation)]\n");
		return -1;
	}

	char *arg_string = argv[1];

	/** for sequence mempool test */
	if (strchr(arg_string, 's') != NULL) {
		/* use mempool malloc/free */
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[s]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)dlib_mpool_malloc(mp,
					(i + j + 1) % max_chunk_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				dlib_mpool_free(mp, array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[s]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)malloc((i + j + 1) %
							  max_chunk_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				free(array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[s]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

	}

	/** for random mempool test */
	if (strchr(arg_string, 'r') != NULL) {
		dlib_rand_init();

		/* use mempool malloc/free */
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[r]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)dlib_mpool_malloc(mp,
					dlib_rand32() % max_chunk_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				dlib_mpool_free(mp, array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[r]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)malloc(dlib_rand32() %
							  max_chunk_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				free(array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[r]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_rand_exit();
	}

	uint32_t fixed_size = 123;

	/** for fixed-size mempool test */
	if (strchr(arg_string, 'f') != NULL) {
		/* use mempool malloc/free */
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[f]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)dlib_mpool_malloc(mp, fixed_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				dlib_mpool_free(mp, array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[f]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < LOOP_NUM; i++) {
			for (j = 0; j < ARRAY_LEN; j++) {
				array[j] = (char *)malloc(fixed_size);
			}
			for (j = 0; j < ARRAY_LEN; j++) {
				free(array[j]);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[f]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);
	}

	dlib_tpool_t *tp;
	uint32_t tp_init_num = 4;
	uint32_t tp_max_num = 8;

	/** for sequence-multi-thread mempool test */
	if (strchr(arg_string, 'S') != NULL) {
		tp = dlib_tpool_init(tp_init_num, tp_max_num, NULL, NULL, NULL);
		if (tp == NULL) {
			fprintf(stderr, "[S]Unexpected error for dlib_tpool_init\n");
			return -3;
		}

		/* use mempool malloc/free */
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[S]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *)((i + j + 1) % max_chunk_size);
				dlib_tpool_execute(tp, 2, _tp_mp_malloc_worker, &array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_mp_free_worker, &array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[S]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *)((i + j + 1) % max_chunk_size);
				dlib_tpool_execute(tp, 2, _tp_glibc_malloc_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_glibc_free_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[S]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_tpool_exit(tp);
	}

	/* for random-multi-thread mempool test */
	if (strchr(arg_string, 'R') != NULL) {
		dlib_rand_init();

		/* use mempool malloc/free */
		tp = dlib_tpool_init(tp_init_num, tp_max_num, NULL, NULL, NULL);
		if (tp == NULL) {
			fprintf(stderr, "[R]Unexpected error for dlib_tpool_init\n");
			return -3;
		}

		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[R]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *)(dlib_rand32() % max_chunk_size);
				dlib_tpool_execute(tp, 2, _tp_mp_malloc_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_mp_free_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[R]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *)(dlib_rand32() % max_chunk_size);
				dlib_tpool_execute(tp, 2, _tp_glibc_malloc_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_glibc_free_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[R]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_tpool_exit(tp);
		dlib_rand_exit();
	}

	/** for fixed-size-multi-thread mempool test */
	if (strchr(arg_string, 'F') != NULL) {
		tp = dlib_tpool_init(tp_init_num, tp_max_num, NULL, NULL, NULL);
		if (tp == NULL) {
			fprintf(stderr, "[F]Unexpected error for dlib_tpool_init\n");
			return -3;
		}

		/* use mempool malloc/free */
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[F]Unexpected error for dlib_mpool_init\n");
			return -2;
		}

		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *)(fixed_size);
				dlib_tpool_execute(tp, 2, _tp_mp_malloc_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_mp_free_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[F]use mempool call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_mpool_exit(mp);

		/* use glibc malloc/free */
		DLIB_UTIMER_RECORD(&start);
		for (i = 0; i < TP_LOOP_NUM; i++) {
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				array[j] = (char *) (fixed_size);
				dlib_tpool_execute(tp, 2, _tp_glibc_malloc_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
			for (j = 0; j < TP_ARRAY_LEN; j++) {
				dlib_tpool_execute(tp, 2, _tp_glibc_free_worker,
						&array[j]);
			}
			dlib_tpool_wait(tp);
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);
		fprintf(stderr, "[F]use glibc call: time %06f\n",
			result.tv_sec + (float) result.tv_usec / 1000000);

		dlib_tpool_exit(tp);
	}

	/** for validable mempool test */
	if (strchr(arg_string, 'v') != NULL) {
		mp = dlib_mpool_init(min, max, factor);
		if (mp == NULL) {
			fprintf(stderr, "[v]dlib_mpool_init error\n");
			return -1;
		}

		char *p1 = (char *) dlib_mpool_malloc(mp, 14);
		memset(p1, 'a', 13);

		fprintf(stderr, "[v]p1[%p] is %s\n", p1, p1);

		dlib_mpool_free(mp, p1);

		char *p2 = (char *) dlib_mpool_malloc(mp, 13);
		fprintf(stderr,
			"--------------------------------------------------\n");
		char *p3 = (char *) dlib_mpool_malloc(mp, 15);

		fprintf(stderr, "[v]p2[%p], p3[%p]\n", p2, p3);

		dlib_mpool_free(mp, p2);

		char *p4 = (char *) dlib_mpool_malloc(mp, 2);

		fprintf(stderr, "[v]p4 [%p]\n", p4);

		dlib_mpool_free(mp, p3);
		fprintf(stderr,
			"--------------------------------------------------\n");
		dlib_mpool_free(mp, p4);

		char *p5 = (char *) dlib_mpool_malloc(mp, 32);
		fprintf(stderr,
			"--------------------------------------------------\n");
		char *p6 = (char *) dlib_mpool_malloc(mp, 3);

		fprintf(stderr, "[v]p5 [%p], p6[%p]\n", p5, p6);
		dlib_mpool_free(mp, p5);
		fprintf(stderr,
			"--------------------------------------------------\n");
		dlib_mpool_free(mp, p6);

		dlib_mpool_exit(mp);
	}

	return 0;
}

void *_tp_mp_malloc_worker(void *targ, void *arg)
{
	UNUSED_PARAM(targ);
	uint32_t size = *(uint32_t *) arg;

	*(char **) arg = (char *) dlib_mpool_malloc(mp, size);

	return NULL;
}

void *_tp_mp_free_worker(void *targ, void *arg)
{
	UNUSED_PARAM(targ);
	dlib_mpool_free(mp, *(char **) arg);

	return NULL;
}

void *_tp_glibc_malloc_worker(void *targ, void *arg)
{
	UNUSED_PARAM(targ);
	uint32_t size = *(uint32_t *) arg;

	*(char **) arg = (char *) malloc(size);

	return NULL;
}

void *_tp_glibc_free_worker(void *targ, void *arg)
{
	free(*(char **) arg);

	return NULL;
}
